package v1

import (
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"gitlab.local/backend/proto/dto"
	"go-micro.dev/v4/client"
	"manager/bll"
	"manager/client/grpc"
	"manager/lib/infra"
	"manager/lib/trace_log"
	"manager/model"
	"manager/model/po"
	"manager/server/web/middleware"
	"manager/store/postgres"
	"manager/utils"
)

var Setting = &setting{}

func init() {
	// 注册路由
	RegisterRouter(Setting)
}

type setting struct{}

func (a *setting) Init(r *gin.RouterGroup) {
	g := r.Group("/setting", middleware.Auth())
	{
		g.POST("/create", a.create)
		g.POST("/update", a.update)
		g.POST("/list", a.list)
		g.POST("/delete", a.delete)
		g.POST("/get", a.get)
	}

	appG := r.Group("app/setting", middleware.Auth())
	{
		appG.POST("/create", a.create)
		appG.POST("/update", a.update)
		appG.POST("/list", a.list)
		appG.POST("/delete", a.delete)
		appG.POST("/get", a.get)
	}
}

func (a *setting) create(c *gin.Context) {
	var (
		in  = &model.SettingCreateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Setting.Create(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}

func (a *setting) update(c *gin.Context) {
	var (
		in  = &model.SettingUpdateRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	settings, _ := postgres.Setting.Find(&model.SettingUpdateRequest{
		Id:      in.Id,
		EbikeId: in.EbikeId,
	})

	bikeInfo, _ := bll.CacheBike.GetBikeInfoById(settings.EbikeId)
	if bikeInfo.SNCode == "" {
		c.Error(fmt.Errorf("车辆不存在"))
		return
	}

	trace_log.DebugLogger(bikeInfo.SNCode, "更新车辆设置 in:", utils.ToJsonString(in), "settings:", utils.ToJsonString(settings))

	// 关闭电子围栏
	if in.EbikeSet != nil && in.EbikeSet.ElectronicFence != nil && *in.EbikeSet.ElectronicFence == 0 {
		// 设备状态，锁定状态时，下发解锁指令
		isLock := bll.Ebike.IsLock(bikeInfo.ID)
		isOverElectronicFence := bll.Setting.IsOverElectronicFence(bikeInfo.SNCode, bikeInfo.ID)
		trace_log.DebugLogger(bikeInfo.SNCode, "settings update isLock:", isLock)
		if isLock && isOverElectronicFence {
			// 下发解锁指令
			cmd := &dto.CommandRequest{
				Sn:  bikeInfo.SNCode,
				Cmd: dto.CmdType_CmdUnlock,
			}

			// 执行指令
			err = bll.RpcCall(bikeInfo.SNCode, func(addr string, err error) error {
				if err != nil {
					return err
				}
				_, err = grpc.Cmd.Command(c, cmd, func(options *client.CallOptions) {
					options.Address = []string{addr}
				})
				return err
			})

			if err != nil {
				trace_log.DebugLogger(bikeInfo.SNCode, "下发解锁指令失败")
				c.Error(fmt.Errorf("下发解锁指令失败"))
				return
			}

			trace_log.DebugLogger(bikeInfo.SNCode, "关闭围栏，下发解锁指令成功")
		}
	}

	// 修改电子围栏大小,开启的状态下
	if settings.EbikeSet != nil && settings.EbikeSet.ElectronicFence > 0 {
		infra.GCache.Delete(fmt.Sprintf("settings_%v", bikeInfo.ID))

		// 围栏大小是否有变更
		if in.EbikeSet.FenceRadius != nil && settings.EbikeSet.FenceRadius != *in.EbikeSet.FenceRadius {
			// 获取当前车辆位置，和围栏中心点位置，计算是否在围栏内
			// 获取车辆位置
			trace_log.DebugLogger(bikeInfo.SNCode, "围栏大小有变更，获取车辆位置")
			rsp, _ := grpc.SCmd.GetShadowAttributeKeys(c, &dto.GetShadowAttributeKeysRequest{
				DeviceName: bikeInfo.SNCode,
				Key:        "location",
			})

			var point po.Point
			err = json.Unmarshal([]byte(rsp.Value), &point)
			if err != nil {
				trace_log.DebugLogger(bikeInfo.SNCode, "获取车辆位置失败")
				c.Error(fmt.Errorf("获取车辆位置失败"))
				return
			}

			// 计算距离
			distance := infra.Lbs.GeoDistance(settings.EbikeSet.FenceCenter.X, settings.EbikeSet.FenceCenter.Y, point.X, point.Y, "K")

			// 默认50米
			if settings.EbikeSet.FenceRadius == 0 {
				settings.EbikeSet.FenceRadius = 10000
			}

			// 如果围栏变更后，车辆在围栏且锁定，下发解锁指令
			status := bll.Ebike.DeviceStatus(c, settings.EbikeId)
			trace_log.DebugLogger(bikeInfo.SNCode, "distance", distance, " point:", utils.ToJsonString(point), " status:", utils.ToJsonString(status))
			if distance <= float64(*in.EbikeSet.FenceRadius) && status.LockStatus == 1 {
				// 下发解锁指令
				cmd := &dto.CommandRequest{
					Sn:  bikeInfo.SNCode,
					Cmd: dto.CmdType_CmdUnlock,
				}

				// 执行指令
				err = bll.RpcCall(bikeInfo.SNCode, func(addr string, err error) error {
					if err != nil {
						return err
					}
					_, err = grpc.Cmd.Command(c, cmd, func(options *client.CallOptions) {
						options.Address = []string{addr}
					})
					return err
				})

				if err != nil {
					trace_log.DebugLogger(bikeInfo.SNCode, "下发解锁指令失败")
					c.Error(fmt.Errorf("下发解锁指令失败"))
					return
				}
				trace_log.DebugLogger(bikeInfo.SNCode, "修改围栏半径，下发解锁指令成功")
			}
		}
	}

	if err = bll.Setting.Update(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}

	utils.ResponseOk(c, nil)
}

func (a *setting) list(c *gin.Context) {
	var (
		in  = &model.SettingListRequest{}
		out = &model.SettingListResponse{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Setting.List(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, out)
}

func (a *setting) get(c *gin.Context) {
	var (
		in  = &model.SettingListRequest{}
		out = &model.SettingListResponse{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if out, err = bll.Setting.List(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}

	if len(out.List) == 0 {
		utils.ResponseOk(c, nil)
		return
	}

	utils.ResponseOk(c, out.List[0])
}

func (a *setting) delete(c *gin.Context) {
	var (
		in  = &model.SettingDeleteRequest{}
		err error
	)

	if err = c.ShouldBindJSON(in); err != nil {
		c.Error(err)
		return
	}

	if err = bll.Setting.Delete(c.Request.Context(), in); err != nil {
		c.Error(err)
		return
	}
	utils.ResponseOk(c, nil)
}
